package de.jmda.gen.java.impl;

import java.util.ArrayList;
import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import de.jmda.core.util.StringUtil;
import de.jmda.gen.Generator;
import de.jmda.gen.GeneratorException;
import de.jmda.gen.impl.AbstractCompoundGenerator;
import de.jmda.gen.java.DeclaredTypeGenerator;
import de.jmda.gen.java.ExtendsClauseGenerator;
import de.jmda.gen.java.ImplementsClauseGenerator;
import de.jmda.gen.java.ModifiersUtil;
import de.jmda.gen.java.TypeBodyGenerator;
import de.jmda.gen.java.TypeKindGenerator;
import de.jmda.gen.java.TypeModifiersGenerator;
import de.jmda.gen.java.TypeNameGenerator;
import de.jmda.gen.java.TypeParameterListGenerator;

public class DefaultDeclaredTypeGenerator
		extends AbstractCompoundGenerator
		implements DeclaredTypeGenerator
{
	public final static String DEFAULT_TYPE_NAME = "DEFAULT_TYPE_NAME";

	private TypeModifiersGenerator modifiersGenerator;
	private @NotNull TypeKindGenerator typeKindGenerator;
	private @NotNull TypeNameGenerator typeNameGenerator;
	private TypeParameterListGenerator typeParameterListGenerator;
	private ExtendsClauseGenerator extendsClauseGenerator;
	private ImplementsClauseGenerator implementsClauseGenerator;
	private @NotNull TypeBodyGenerator typeBodyGenerator;

	public DefaultDeclaredTypeGenerator()
	{
		this(null, null, null, null, null, null, null);
	}

	public DefaultDeclaredTypeGenerator(
      TypeModifiersGenerator modifiersGenerator,
      TypeKindGenerator typeKindGenerator,
      TypeNameGenerator typeNameGenerator,
      TypeParameterListGenerator typeParameterListGenerator,
      ExtendsClauseGenerator extendsClauseGenerator,
      ImplementsClauseGenerator implementsClauseGenerator,
      TypeBodyGenerator typeBodyGenerator)
	{
		super();

		setModifiersGenerator(modifiersGenerator);
		setTypeKindGenerator(notNull(typeKindGenerator));
		setTypeNameGenerator(notNull(typeNameGenerator));
		setTypeParameterListGenerator(typeParameterListGenerator);
		setExtendsClauseGenerator(extendsClauseGenerator);
		setImplementsClauseGenerator(implementsClauseGenerator);
		setTypeBodyGenerator(notNull(typeBodyGenerator));

		setComponentSeparator(" ");
	}

	/**
	 * Returns collection of <code>Generator</code>s with the return values of the
	 * following methods in the following order:
	 * <ul>
	 *   <li>{@link #getModifiersGenerator()}</li>
	 *   <li>{@link #getTypeKindGenerator()}</li>
	 *   <li>{@link #getTypeNameGenerator()}</li>
	 *   <li>{@link #getTypeParameterListGenerator()}</li>
	 *   <li>{@link #getExtendsClauseGenerator()}</li>
	 *   <li>{@link #getImplementsClauseGenerator()}</li>
	 *   <li>{@link #getTypeBodyGenerator()}</li>
	 * </ul>
	 * 
	 * @see de.jmda.gen.CompoundGenerator#getGeneratorComponents()
	 */
	@Override
  @NotNull
  @Valid
	public List<Generator> getGeneratorComponents()
	{
		List<Generator> result = new ArrayList<Generator>();

		result.add(getModifiersGenerator());
		result.add(getTypeKindGenerator());
		result.add(getTypeNameGenerator());
		result.add(getTypeParameterListGenerator());
		result.add(getExtendsClauseGenerator());
		result.add(getImplementsClauseGenerator());
		result.add(dg(System.lineSeparator()));
		result.add(getTypeBodyGenerator());

		return result;
	}

	@Override
  public StringBuffer generate() throws GeneratorException
  {
		StringBuffer result =
				StringUtil.replace(
						super.generate(),
						" <",
						"<");
		
		result =
				StringUtil.replace(
						result,
						" {",
						"{");

		result =
				StringUtil.replace(
						result,
						" " + System.lineSeparator(),
						System.lineSeparator());

		return result;
  }

	/**
	 * @return {@link #modifiersGenerator}, may be <code>null</code> because
	 *         modifiers are optional.
	 *
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#getModifiersGenerator()
	 */
	@Override
	public TypeModifiersGenerator getModifiersGenerator()
	{
		return modifiersGenerator;
	}

	@Override
	public void setModifiersGenerator(TypeModifiersGenerator generator)
	{
		modifiersGenerator = generator;
	}

	/**
	 * @return {@link #typeKindGenerator}, if type kind generator is <code>null
	 *         </code> it will be lazily initialised because type kind is
	 *         mandatory.
	 *
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#getTypeKindGenerator()
	 */
	@Override
	@NotNull
	public TypeKindGenerator getTypeKindGenerator()
	{
		return notNull(typeKindGenerator);
	}

	@Override
	public void setTypeKindGenerator(@NotNull TypeKindGenerator generator)
	{
		typeKindGenerator = notNull(generator);
	}

	/**
	 * @return {@link #typeNameGenerator}, if type name generator is <code>null
	 *         </code> it will be lazily initialised because type name is
	 *         mandatory.
	 *
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#getTypeNameGenerator()
	 */
	@Override
	public TypeNameGenerator getTypeNameGenerator()
	{
		return notNull(typeNameGenerator);
	}

	@Override
	public void setTypeNameGenerator(@NotNull TypeNameGenerator generator)
	{
		typeNameGenerator = notNull(generator);
	}

	/**
	 * @return {@link #typeParameterListGenerator}, may be <code>null</code>
	 *         because a parameter list is optional.
	 *
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#getTypeParameterListGenerator()
	 */
	@Override
	public TypeParameterListGenerator getTypeParameterListGenerator()
	{
		return typeParameterListGenerator;
	}

	@Override
	public void setTypeParameterListGenerator(TypeParameterListGenerator generator)
	{
		typeParameterListGenerator = generator;
	}

	/**
	 * @return {@link #extendsClauseGenerator}, may be <code>null</code> because
	 *         an extends clause is optional.
	 *
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#getExtendsClauseGenerator()
	 */
	@Override
	public ExtendsClauseGenerator getExtendsClauseGenerator()
	{
		return extendsClauseGenerator;
	}

	@Override
	public void setExtendsClauseGenerator(ExtendsClauseGenerator generator)
	{
		extendsClauseGenerator = generator;
	}

	/**
	 * @return {@link #implementsClauseGenerator}, may be <code>null</code>
	 *         because an implements clause is optional.
	 *
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#getImplementsClauseGenerator()
	 */
	@Override
	public ImplementsClauseGenerator getImplementsClauseGenerator()
	{
		return implementsClauseGenerator;
	}

	@Override
	public void setImplementsClauseGenerator(ImplementsClauseGenerator generator)
	{
		implementsClauseGenerator = generator;
	}

	/**
	 * @return {@link #typeBodyGenerator}, if type body generator is <code>
	 *         null</code> it will be lazily initialised because type body is
	 *         mandatory.
	 *
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#getTypeBodyGenerator()
	 */
	@Override
	@NotNull
	public TypeBodyGenerator getTypeBodyGenerator()
	{
		return notNull(typeBodyGenerator);
	}

	@Override
	public void setTypeBodyGenerator(@NotNull TypeBodyGenerator generator)
	{
		typeBodyGenerator = notNull(generator);
	}

	@Override
	@NotNull
	public String getTypeName() throws GeneratorException
	{
		return notNull(typeNameGenerator).getTypeName();
	}

	@Override
	public void setTypeName(@NotNull String typeName)
	{
		notNull(typeNameGenerator).setTypeName(typeName);
	}

	/**
	 * @NotNull
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#demandModifiersGenerator()
	 */
	@Override
  public TypeModifiersGenerator demandModifiersGenerator()
  {
		modifiersGenerator = notNull(modifiersGenerator);
	  return modifiersGenerator;
  }

	/**
	 * @NotNull
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#demandTypeParameterListGenerator()
	 */
	@Override
  public TypeParameterListGenerator demandTypeParameterListGenerator()
  {
		typeParameterListGenerator = notNull(typeParameterListGenerator);
	  return typeParameterListGenerator;
  }

	/**
	 * @NotNull
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#demandExtendsClauseGenerator()
	 */
	@Override
  public ExtendsClauseGenerator demandExtendsClauseGenerator()
  {
		extendsClauseGenerator = notNull(extendsClauseGenerator);
	  return extendsClauseGenerator;
  }

	/**
	 * @NotNull
	 * @see de.jmda.gen.java.DeclaredTypeGenerator#demandImplementsClauseGenerator()
	 */
	@Override
  public ImplementsClauseGenerator demandImplementsClauseGenerator()
  {
		implementsClauseGenerator = notNull(implementsClauseGenerator);
	  return implementsClauseGenerator;
  }

	/**
	 * @param stringBuffer
	 * @return <code>stringBuffer</code>
	 * @throws GeneratorException if <code>stringBuffer</code> does not contain
	 *                            {@link ModifiersUtil#ABSTRACT}
	 */
	protected StringBuffer validate(StringBuffer stringBuffer)
			throws GeneratorException
	{
		if (false == ModifiersUtil.hasAbstractModifier(stringBuffer))
		{
			throw new GeneratorException(
					  stringBuffer + " has to contain [" + ModifiersUtil.ABSTRACT + "] "
					+ "modifier");
		}

		return stringBuffer;
	}

	private TypeKindGenerator notNull(TypeKindGenerator generator)
	{
		if (generator == null)
		{
			generator = TypeKindGenerator.CLASS;
		}

		return generator;
	}

	private TypeNameGenerator notNull(TypeNameGenerator generator)
	{
		if (generator == null)
		{
			generator = new DefaultTypeNameGenerator();
			generator.setTypeName(DEFAULT_TYPE_NAME);
		}

		return generator;
	}

	private TypeBodyGenerator notNull(TypeBodyGenerator generator)
	{
		if (generator == null)
		{
			generator = new DefaultTypeBodyGenerator();
		}

		return generator;
	}

	private ExtendsClauseGenerator notNull(ExtendsClauseGenerator generator)
  {
		if (generator == null)
		{
			generator = new DefaultExtendsClauseGenerator();
		}

		return generator;
  }

	private TypeModifiersGenerator notNull(TypeModifiersGenerator generator)
  {
		if (generator == null)
		{
			generator = new DefaultTypeModifiersGenerator();
		}

		return generator;
  }

	private TypeParameterListGenerator notNull(TypeParameterListGenerator generator)
  {
		if (generator == null)
		{
			generator = new DefaultTypeParameterListGenerator();
		}

		return generator;
  }

	private ImplementsClauseGenerator notNull(ImplementsClauseGenerator generator)
  {
		if (generator == null)
		{
			generator = new DefaultImplementsClauseGenerator();
		}

		return generator;
  }
}